package org.naxx.security.parser;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import org.naxx.security.algorithm.RuleCombiningAlgorithm;
import org.naxx.security.model.Policy;
import org.naxx.security.model.impl.PolicyImpl;
import org.naxx.security.model.impl.expression.VariableDefinitionImpl;
import org.naxx.security.parser.util.QNames;

public class PolicyParser extends AbstractXmlParser<Policy> {
	
	private RuleParser ruleParser;
	private ApplyParser applyParser;
	private TargetParser targetParser;
	
	public void setRuleParser(RuleParser ruleParser) {
		this.ruleParser = ruleParser;
	}
	public void setApplyParser(ApplyParser applyParser) {
		this.applyParser = applyParser;
	}
	public void setTargetParser(TargetParser targetParser) {
		this.targetParser = targetParser;
	}
	
	/**
	 * Precondition: Cursor points to element just before opening Policy element.<br>
	 * Postcondition: Cursor points to closing Policy element.
	 */
	public Policy parse(XMLEventReader reader) throws Exception {
		final PolicyImpl policy = new PolicyImpl();
		
		XMLEvent event = reader.nextTag(); // now at opening Policy
		StartElement start = event.asStartElement();
		Attribute policyIdAttr = start.getAttributeByName(QNames.POLICY_ID_ATTRIBUTE);
		Attribute versionAttr = start.getAttributeByName(QNames.VERSION_ATTRIBUTE);
		Attribute ruleCombiningAlgorAttr = start.getAttributeByName(QNames.RULE_COMBINING_ALGORITHM_ID_ATTRIBUTE);
		policy.setId(policyIdAttr.getValue());
		policy.setRuleCombiningAlgorithm(RuleCombiningAlgorithm.resolve(ruleCombiningAlgorAttr.getValue()));
		if(versionAttr != null) {
			policy.setVersion(versionAttr.getValue());
		}
		
		event = reader.nextTag();
		start = event.asStartElement();
		if("Description".equals(start.getName().getLocalPart())) {
			StringBuilder buffer = new StringBuilder();
			event = this.getCharacters(reader, buffer);
			policy.setDescription(buffer.toString().trim());
			event = reader.nextTag();
			start = event.asStartElement();
		}
		// TODO not implemented
		if("PolicyDefaults".equals(start.getName().getLocalPart())) {
			loop:
			while(true) {
				event = reader.nextEvent();
				if(event.isEndElement() && "PolicyDefaults".equals(event.asEndElement()
						.getName().getLocalPart())) {
					break loop;
				}
			}
			event = reader.nextTag();
			start = event.asStartElement();
		}
		// TODO not implemented
		if("CombinerParameters".equals(start.getName().getLocalPart())) {
			loop:
			while(true) {
				event = reader.nextEvent();
				if(event.isEndElement() && "CombinerParameters".equals(event.asEndElement()
							.getName().getLocalPart())) {
					break loop;
				}
			}
			event = reader.nextTag();
			start = event.asStartElement();
		}
		
		policy.setTarget(this.targetParser.parse(reader));
		
		loop:
		while(true) {
			if(reader.peek().getEventType() == XMLStreamConstants.START_ELEMENT) {
				if("Rule".equals(reader.peek().asStartElement().getName().getLocalPart())) {
					policy.addRule(this.ruleParser.parse(reader));
				}
				else if("VariableDefinition".equals(reader.peek().asStartElement()
						.getName().getLocalPart())) {
					event = reader.nextTag(); // we now at VariableDefinition element
					Attribute variableIdAttr = event.asStartElement().getAttributeByName(
							QNames.VARIABLE_ID_ATTRIBUTE);
					VariableDefinitionImpl variableDefinition = new VariableDefinitionImpl();
					variableDefinition.setVariableId(variableIdAttr.getValue());
					variableDefinition.setExpressionTree(this.applyParser.parse(reader));
					
					policy.addVariableDefinition(variableDefinition);
				}
			}
			event = reader.nextEvent();
			if(event.isEndElement() && "Policy".equals(event.asEndElement().getName().getLocalPart())) {
				break loop;
			}
		}
		return policy;
	}
}
